package com.wfh.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.wfh.ai.AiCodeGenTypeRouteService;
import com.wfh.ai.AiCodeGenTypeRoutingServiceFactory;
import com.wfh.common.ErrorCode;
import com.wfh.constant.AppConstant;
import com.wfh.core.AiCodeGenFacade;
import com.wfh.core.builder.VueProjectBuilder;
import com.wfh.core.handler.StreamHandlerExecutor;
import com.wfh.exception.BusinessException;
import com.wfh.exception.ThrowUtils;
import com.wfh.inner.InnerScreenShotService;
import com.wfh.inner.InnerUserService;
import com.wfh.mapper.AppMapper;
import com.wfh.model.dto.AppAddRequest;
import com.wfh.model.dto.AppQueryRequest;
import com.wfh.model.entity.App;
import com.wfh.model.entity.User;
import com.wfh.model.enums.ChatHistoryMessageTypeEnum;
import com.wfh.model.enums.CodeGenTypeEnums;
import com.wfh.model.vo.AppVO;
import com.wfh.model.vo.UserVO;
import com.wfh.service.AppService;
import com.wfh.service.ChatHistoryService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author wangfenghuan
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @DubboReference
    private InnerUserService userService;

    @Resource
    private AiCodeGenFacade aiCodeGenFacade;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Resource
    @Lazy
    private AppService appService;

    @Resource
    private ChatHistoryService chatHistoryService;

    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;

    @Resource
    @Lazy
    private AiCodeGenTypeRoutingServiceFactory aiCodeGenTypeRoutingServiceFactory;

    @DubboReference
    private InnerScreenShotService screenshotService;


    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("cover", cover)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }

    /**
     * 生成应用 flux
     * @param appId
     * @param prompt
     * @param loginUser
     * @return
     */
    @Override
    public Flux<String> chatToGenCodeFlux(Long appId, String prompt, User loginUser) {
        // 参数校验
        if (appId == null || StrUtil.isBlank(prompt)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        // 查询应用信息
        App byId = this.getById(appId);
        if (byId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用不存在");
        }
        if (!byId.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "无权限操作");
        }
        String codeGenType = byId.getCodeGenType();
        CodeGenTypeEnums byValue = CodeGenTypeEnums.getByValue(codeGenType);
        if (byValue == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用类型不合法");
        }
        // 调用ai之前，先保存用户消息到数据库中
        chatHistoryService.addChatMessage(appId, prompt, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        // 调用ai生成代码
        Flux<String> stringFlux = aiCodeGenFacade.genAndSaveCodeWithFlux(prompt, byValue, appId);
       return streamHandlerExecutor.doExecute(stringFlux, chatHistoryService, appId, loginUser, byValue);
    }

    /**
     * 删除应用时关联删除对话历史
     *
     * @param id 应用ID
     * @return 是否成功
     */
    @Override
    public boolean removeById(Serializable id) {
        if (id == null) {
            return false;
        }
        // 转换为 Long 类型
        long appId = Long.parseLong(id.toString());
        if (appId <= 0) {
            return false;
        }
        // 先删除关联的对话历史
        try {
            chatHistoryService.deleteByAppId(appId);
        } catch (Exception e) {
            // 记录日志但不阻止应用删除
            log.error("删除应用关联对话历史失败: {}", e.getMessage());
        }
        // 删除应用
        return super.removeById(id);
    }


    /**
     * 应用部署
     *
     * @param appId
     * @param loginUser
     * @return
     */
    @Override
    public String deploy(Long appId, User loginUser) {
        // 参数校验
        if (appId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        // 查询应用信息
        App byId = this.getById(appId);
        if (byId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用不存在");
        }
        // 校验权限
        if (!loginUser.getId().equals(byId.getUserId())) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "无权限操作");
        }
        // 检查是否已经有deploykey
        String deployKey = byId.getDeployKey();
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        String codeGenType = byId.getCodeGenType();
        String sourceDirName = codeGenType + "_" + "project" + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 检查目录是否存在
        File file = new File(sourceDirPath);
        if (!file.exists()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "代码目录不存在");
        }
        // vue项目特殊处理，
        if (CodeGenTypeEnums.VUE.getValue().equals(codeGenType)) {
            // vue项目构建
            boolean b = vueProjectBuilder.buildProjectAsync(sourceDirPath, appId);
            ThrowUtils.throwIf(!b, ErrorCode.SYSTEM_ERROR, "构建失败，请重试");
        }
        // 验证dist目录是否存在
        File distDir = new File(sourceDirPath, "dist");
        ThrowUtils.throwIf(!distDir.exists(), ErrorCode.SYSTEM_ERROR, "项目构建完成但是没生成dist目录");
        // 构建完成之后，将文件复制到部署目录
        file = distDir;
        // 复制文件到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(file, new File(deployDirPath), true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "部署失败");
        }
        App app = new App();
        app.setId(appId);
        app.setDeployKey(deployKey);
        app.setDeployedTime(LocalDateTime.now());
        boolean b = this.updateById(app);
        // 10. 构建应用访问 URL
        String appDeployUrl = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
        // 11. 异步生成截图并更新应用封面
        generateAppScreenshotAsync(appId, appDeployUrl);
        return appDeployUrl;
    }

    /**
     * 生成应用截图
     * @param appId
     * @param appDeployUrl
     */
    private void generateAppScreenshotAsync(Long appId, String appDeployUrl) {
        // 使用虚拟线程异步执行
        Thread.startVirtualThread(() -> {
            // 调用截图服务生成截图并上传截图
            String screenshot = screenshotService.generateAndUploadScreenshot(appDeployUrl);
            // 更新应用封面字段
            App app = new App();
            app.setId(appId);
            app.setCover(screenshot);
            boolean b = this.updateById(app);
            if (!b){
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新应用封面失败");
            }
        });
    }

    /**
     * 生成应用
     * @param appId
     * @param prompt
     * @param loginUser
     * @return
     */
    @Override
    public String chatToGenCode(Long appId, String prompt, User loginUser) {
        // 参数校验
        if (appId == null || StrUtil.isBlank(prompt)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        // 查询应用信息
        App byId = this.getById(appId);
        if (byId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用不存在");
        }
        if (!byId.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "无权限操作");
        }
        String codeGenType = byId.getCodeGenType();
        CodeGenTypeEnums byValue = CodeGenTypeEnums.getByValue(codeGenType);
        if (byValue == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用类型不合法");
        }
        File file = aiCodeGenFacade.genAndSaveCode(prompt, byValue, appId);
        return null;
    }


    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取用户信息，避免 N+1 查询问题
        Set<Long> userIds = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        Map<Long, UserVO> userVOMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, userService::getUserVO));
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            UserVO userVO = userVOMap.get(app.getUserId());
            appVO.setUser(userVO);
            return appVO;
        }).collect(Collectors.toList());
    }


    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        // 关联查询用户信息
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(userId);
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            appVO.setUser(userVO);
        }
        return appVO;
    }

    /**
     * 添加应用
     * @param request
     * @return
     */
    @Override
    public Long addApp(AppAddRequest appAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_ERROR);
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");
        // 获取当前登录用户
        User loginUser = InnerUserService.getLoginUser(request);
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        // 应用名称暂时为 initPrompt 前 12 位
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        // 暂时设置为多文件生成
        AiCodeGenTypeRouteService aiCodeGenTypeRoutingService = aiCodeGenTypeRoutingServiceFactory.createAiCodeGenTypeRoutingService();
        CodeGenTypeEnums codeGenTypeEnums = aiCodeGenTypeRoutingService.routeGenType(initPrompt);
        app.setCodeGenType(codeGenTypeEnums.getValue());
        // 插入数据库
        boolean result = appService.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return app.getId();
    }

}
